package com.bflow.impl;

import com.bflow.api.*;
import com.bflow.api.bo.*;
import com.bflow.api.constant.BflowConstant;
import com.bflow.api.constant.BflowConstant.CommDefineActRouteCondDesc;
import com.bflow.api.constant.BflowConstant.RunTaskStatus;
import com.bflow.commons.bean.PageResult;
import com.bflow.commons.consts.CommConst;
import com.bflow.commons.exception.BizException;
import com.bflow.commons.util.StrUtil;
import com.bflow.util.AssertUtil;
import com.bflow.util.BeanMapper;
import com.bflow.util.JsonUtil;
import com.bflow.util.SysSeqUtil;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;

import static com.bflow.commons.consts.CommConst.INIT_VERSION;

/**
 * 功能：DefaultAuditTemplateServiceImpl 系统默认审批模板-实现业务接口
 * 作者：liaofeng
 * 日期：2018-01-03
 */
@Service("defaultAuditTemplateService")
public class DefaultAuditTemplateServiceImpl implements IDefaultAuditTemplateService {

    @Autowired
    private IBflowDefineProcessService<?, BflowDefineProcessBo> iBflowDefineProcessService;

    @Autowired
    private IBflowDefineActService<?, BflowDefineActBo> iBflowDefineActService;

    @Autowired
    private IBflowRunTaskService<?, BflowRunTaskBo> iBflowRunTaskService;

    @Autowired
    private IBflowRunRecordService<?, BflowRunRecordBo> iBflowRunRecordService;

    @Autowired
    private IBflowRunInstanceService<?, BflowRunInstanceBo> iBflowRunInstanceService;

    @Autowired
    private IBflowDefineActRouteService<?, BflowDefineActRouteBo> iBflowDefineActRouteService;

    @Autowired
    private IBflowExtResService bflowExtResService;

    private final Logger logger = LoggerFactory.getLogger(DefaultAuditTemplateServiceImpl.class);

    private final int pageNo = 1;

    private final int pageSize = 500;

    /**
     * 查询历史记录列表
     *
     * @param queryParam
     * @param busiKey
     * @return
     * @throws BizException
     */
    @Override
    public PageResult<BflowRunInstanceBo> findHistoryList(BflowRunInstanceQueryParam queryParam, String busiKey) throws BizException {
        AssertUtil.isNotBlank(busiKey, "流程业务KEY 不能为空");
        BflowRunInstanceBo queryBo = queryParam.getBoIfNullAndNew();
        queryBo.setBusiKey(busiKey);
        queryParam.setBflowRunInstance(queryBo);
        queryParam.setDefParam(pageNo, pageSize, "CREATE_TIME", "DESC");
        PageResult<BflowRunInstanceBo> pageResult = iBflowRunInstanceService.findPage(queryParam);
        return pageResult;
    }

    /**
     * 查询审批列表
     *
     * @param queryParam
     * @param userKey
     * @param busiKey
     * @return
     * @throws BizException
     */
    @Override
    public PageResult<BflowRunTaskBo> findAuditList(BflowRunTaskQueryParam queryParam, String userKey, String busiKey) throws BizException {
        AssertUtil.isNotBlank(userKey, "用户未登录");
        AssertUtil.isNotBlank(busiKey, "流程业务KEY 不能为空");
        BflowRunTaskBo queryBo = queryParam.getBoIfNullAndNew();
        queryBo.setDelFlag(CommConst.STATUS_Y);
        queryBo.setStatus(RunTaskStatus.WAITING_DEAL.getCode());
        queryBo.setUserKey(userKey);

        BflowRunInstanceBo queryInstance = new BflowRunInstanceBo();
        queryInstance.setBusiKey(busiKey);

        queryBo.setInstance(queryInstance);
        queryParam.setBflowRunTask(queryBo);
        queryParam.setDefParam(pageNo, pageSize, "task.CREATE_TIME", "DESC");

        PageResult<BflowRunTaskBo> pageResult = iBflowRunTaskService.findTaskInstancePage(queryParam);
        return pageResult;
    }

    /**
     * 提交申请
     *
     * @param instanceBo
     * @param auditUserIds
     * @param defActKey
     * @param defineProcessBo
     * @throws BizException
     */
    @Transactional
    @Override
    public void addApply(BflowRunInstanceBo instanceBo, String auditUserIds, String defActKey, BflowDefineProcessBo defineProcessBo) throws BizException {
        validAddApply(instanceBo, auditUserIds, defActKey, defineProcessBo);
        String applyUid = instanceBo.getStartUid();
        String applyUname = instanceBo.getStartUname();
        logger.info("开始开启流程,发起人ID:" + applyUid + ",发起人姓名:" + applyUname + ",提交数据:" + JsonUtil.toStr(instanceBo));
        String[] auditUserIdsArray = auditUserIds.split(BflowConstant.ACT_SPLIT);
        AssertUtil.isTrue(ArrayUtils.isNotEmpty(auditUserIdsArray), "审批人集合不存在,流程无法进行！");
        String[] actSet = getActSet(auditUserIdsArray, defActKey);
        //新增流程
        BflowDefineProcessBo processBo = addProcess(applyUname, actSet, auditUserIdsArray, actSet[0], defineProcessBo);
        instanceBo.setProcKey(BflowConstant.PROCESS_KEY_TPL_PREFIX + processBo.getId()); //以tpl_ 开头来区分
        instanceBo.setBusiKey(processBo.getKey());
        iBflowRunInstanceService.startProcess(instanceBo);
        logger.info("结束开启流程,发起人ID:" + applyUid + ",发起人姓名:" + applyUname);
    }

    /**
     * 获取活动集合
     *
     * @param auditUserIds
     * @param defActKey
     * @return
     */
    private String[] getActSet(String[] auditUserIds, String defActKey) {
        //获取 默认审批活动
        BflowDefineActBo defActBo = iBflowDefineActService.getCacheByKey(defActKey);
        AssertUtil.isNotNull(defActBo, "未定义" + defActKey + "活动Key");
        String[] actSet = new String[auditUserIds.length];
        for (int i = 0; i < auditUserIds.length; i++) {
            actSet[i] = defActBo.getId();
        }
        return actSet;
    }


    /**
     * 添加流程
     * @param applyName
     * @param actSet
     * @param actAssigneeSet
     * @param startActId
     * @param defineProcessBo
     * @return
     */
    private BflowDefineProcessBo addProcess(String applyName, String[] actSet, String[] actAssigneeSet, String startActId, BflowDefineProcessBo defineProcessBo) {
        //定义流程
        BflowDefineProcessBo processBo = new BflowDefineProcessBo();
        BeanMapper.copy(defineProcessBo, processBo);
        processBo.setId(SysSeqUtil.genId());
        processBo.setActSet(StrUtil.join(actSet, BflowConstant.ACT_SPLIT));
        processBo.setActAssigneeSet(StrUtil.join(actAssigneeSet, BflowConstant.ACT_SPLIT));
        processBo.setStartActId(startActId);
        processBo.setStatus(CommConst.STATUS_Y);
        processBo.setVersion(INIT_VERSION);
        System.out.println("流程：" + JsonUtil.toStr(processBo));
        //保存流程
        iBflowDefineProcessService.add(processBo);
        return processBo;
    }

    /**
     * 验证申请信息
     *
     * @param instanceBo
     * @param auditUserIds
     * @param defActKey
     * @param defineProcessBo
     */
    private void validAddApply(BflowRunInstanceBo instanceBo, String auditUserIds, String defActKey, BflowDefineProcessBo defineProcessBo) {
        AssertUtil.isNotNull(instanceBo, "提交的申请内容 不能为空");
        AssertUtil.isNotBlank(auditUserIds, "审批人集合 不能为空");
        AssertUtil.isNotBlank(defActKey, "默认的活动KEY 不能为空");
        AssertUtil.isNotNull(defineProcessBo, "流程定义内容 不能为空");
        AssertUtil.isNotBlank(defineProcessBo.getKey(), "流程业务KEY 不能为空");
        AssertUtil.isNotBlank(defineProcessBo.getName(), "流程名称 不能为空");
    }

    /**
     * 审批
     *
     * @param auditBo
     * @param defActKey
     * @throws BizException
     */
    @Transactional
    @Override
    public void audit(BflowRunTaskAuditBo auditBo, String defActKey) throws BizException {
        //验证信息
        this.validAuditInfo(auditBo, defActKey);
        String taskId = auditBo.getTaskId();

        //获取结论，结论实际是活动路由ID
        BflowRunTaskBo taskBo = iBflowRunTaskService.findById(taskId);
        AssertUtil.isNotNull(taskBo, "任务数据不存在,流程无法进行！");
        BflowDefineProcessBo processBo = iBflowDefineProcessService.findById(taskBo.getProcId());
        BflowDefineDefActRouteGroupBo groupBo = getDefActRouteBos(processBo, taskBo.getActSort(), defActKey);

        String instanceId = taskBo.getInstanceId();
        AssertUtil.isNotBlank(instanceId, "业务数据不存在,流程无法进行！");
        //获取活动路由数据
        String actRouteFlag = auditBo.getRouteId();
        BflowDefineActRouteBo actRouteBo = groupBo.getRouteById(actRouteFlag);
        AssertUtil.isNotNull(actRouteBo, "活动路由数据不存在,流程无法进行！");
        //无论审批结果如何，都结束当前的任务
        BflowRunTaskBo finishTaskBo = new BflowRunTaskBo();
        finishTaskBo.setId(taskId);
        finishTaskBo.setStatus(RunTaskStatus.FINISH_DEAL.getCode());
        finishTaskBo.setFinishTime(new Date());
        System.out.println("结束当前的任务：" + JsonUtil.toStr(finishTaskBo));
        iBflowRunTaskService.update(finishTaskBo);

        if (StrUtil.isNotBlank(actRouteBo.getNextActId())) {
            //审批完进入下一步流程
            BflowDefineActRouteBo nextActBo = groupBo.getRouteYes();
            iBflowRunTaskService.addNextTask(nextActBo.getNextActId(), nextActBo.getNextActAssignee(), instanceId, taskBo.getProcId(), auditBo);
        } else {
            System.out.println("结束流程，instanceId为:" + instanceId);
            iBflowRunInstanceService.stopProcess(instanceId);
        }

        //设置记录数据
        BflowRunRecordBo bflowRunRecordBo = new BflowRunRecordBo(auditBo);
        bflowRunRecordBo.setInstanceId(instanceId);
        bflowRunRecordBo.setTaskId(taskId);
        bflowRunRecordBo.setFlag(actRouteBo.getCondDesc());
        bflowRunRecordBo.setVersion(taskBo.getVersion());
        bflowRunRecordBo.setCurActId(taskBo.getActId());
        //插入记录数据
        System.out.println("保存的记录数据：" + JsonUtil.toStr(bflowRunRecordBo));
        iBflowRunRecordService.add(bflowRunRecordBo);

        //更新最终结论
        this.updateRunInstanceFinalConclusion(processBo, taskBo.getActSort(), actRouteBo, groupBo, instanceId);
    }

    /**
     * 验证信息
     *
     * @param auditBo
     * @param defActKey
     */
    private void validAuditInfo(BflowRunTaskAuditBo auditBo, String defActKey) {
        AssertUtil.isNotNull(auditBo, "提交的内容 不能为空");
        AssertUtil.isNotBlank(defActKey, "活动KEY 不能为空");
    }

    /**
     * 查询instance业务数据
     *
     * @param instanceId
     * @param userId
     * @return
     * @throws BizException
     */
    @Override
    public BflowRunInstanceBo detail(String instanceId, String userId) throws BizException {
        AssertUtil.isNotBlank(instanceId, "instanceId 不能为空");
        AssertUtil.isNotBlank(userId, "用户未登录");
        BflowRunInstanceBo instanceBo = iBflowRunInstanceService.findById(instanceId);
        this.getProcessRecord(instanceBo, userId);
        return instanceBo;
    }

    /**
     * 获取流程记录
     *
     * @param instanceBo
     * @param userId
     */
    private void getProcessRecord(BflowRunInstanceBo instanceBo, String userId) {
        if (instanceBo != null) {
            BflowDefineProcessBo processBo = getBflowDefineProcessBo(instanceBo.getProcKey());
            String[] actArray = processBo.getActArray();
            String[] auditUserIds = processBo.getActAssigneeArray();
            List<BflowRunRecordBo> processRecords = iBflowRunRecordService.getFullList(instanceBo, actArray, auditUserIds, userId);
            instanceBo.setRecords(processRecords);
        }
    }

    private BflowDefineProcessBo getBflowDefineProcessBo(String procKey){
        String id = procKey.split(BflowConstant.PROCESS_KEY_TPL_PREFIX)[1];
        return iBflowDefineProcessService.findById(id);
    }


    /**
     * 获取task任务数据
     *
     * @param taskId
     * @param userId
     * @param defActKey
     * @return
     * @throws BizException
     */
    @Override
    public BflowRunTaskBo getRunTaskDetail(String taskId, String userId, String defActKey) throws BizException {
        AssertUtil.isNotBlank(taskId, "taskId 不能为空");
        AssertUtil.isNotBlank(userId, "用户未登录");
        AssertUtil.isNotBlank(defActKey, "活动KEY 不能为空");
        BflowRunTaskBo taskBo = iBflowRunTaskService.findById(taskId);
        if (taskBo != null) {
            AssertUtil.assertEqual(userId, taskBo.getUserKey(), "非法请求，您无法处理此任务");
            //获取instance业务数据
            String instanceId = taskBo.getInstanceId();
            if (StringUtils.isNotBlank(instanceId)) {
                BflowRunInstanceBo instanceBo = this.detail(instanceId, userId);
                if (instanceBo != null) {
                    taskBo.setInstance(instanceBo);
                    BflowDefineProcessBo processBo = getBflowDefineProcessBo(instanceBo.getProcKey());
                    if (processBo != null) {
                        //获取 默认路由
                        taskBo.setActRoutes(getDefActRouteBos(processBo, taskBo.getActSort(), defActKey).toList());
                    }
                }
            }
        }
        return taskBo;
    }


    /**
     * 设置默认活动路由
     *
     * @param processBo
     * @param actSort
     * @param defActKey
     * @return
     */
    private BflowDefineDefActRouteGroupBo getDefActRouteBos(BflowDefineProcessBo processBo, Integer actSort, String defActKey) {
        //获取 默认审批活动
        BflowDefineActBo defActBo = iBflowDefineActService.getCacheByKey(defActKey);
        AssertUtil.isNotNull(defActBo, "未定义" + defActKey + "活动Key");

        String procId = processBo.getId();
        //构造 条件路由 +"_"+actSort
        BflowDefineActRouteBo routeNo = new BflowDefineActRouteBo(CommDefineActRouteCondDesc.DEF_NOPASS.getCode(), procId, actSort, defActBo.getId(), CommDefineActRouteCondDesc.DEF_NOPASS.getTitle());
        BflowDefineActRouteBo routeYes = new BflowDefineActRouteBo(CommDefineActRouteCondDesc.DEF_PASS.getCode(), procId, actSort, defActBo.getId(), CommDefineActRouteCondDesc.DEF_PASS.getTitle());
        //设置 处理人
        String[] actAssigneeArray = processBo.getActAssigneeArray();
        routeYes.setAssignee(actAssigneeArray[actSort]);
        //设置下个活动
        String[] actArray = processBo.getActArray();
        if (actArray.length != (actSort + 1)) {
            routeYes.setNextActId(defActBo.getId() + "_" + (actSort + 1));
            routeYes.setNextActAssignee(actAssigneeArray[actSort + 1]);
        }
        return new BflowDefineDefActRouteGroupBo(routeNo, routeYes);
    }

    /**
     * 更新最终结论
     * @param processBo
     * @param actSort
     * @param actRouteBo
     * @param groupBo
     * @param instanceId
     */
    private void updateRunInstanceFinalConclusion(BflowDefineProcessBo processBo, Integer actSort, BflowDefineActRouteBo actRouteBo
            , BflowDefineDefActRouteGroupBo groupBo, String instanceId){

        String[] actArray = processBo.getActArray();
        if (actArray.length == (actSort + 1)) {

            if(StringUtils.isNotBlank(actRouteBo.getId())){

                BflowDefineActRouteBo routeYes = groupBo.getRouteYes();
                BflowDefineActRouteBo routeNo = groupBo.getRouteNo();
                String finalConclusion = null;

                if(StringUtils.isNotBlank(routeYes.getId()) && actRouteBo.getId().equals(routeYes.getId())){
                    finalConclusion = BflowConstant.RunInstance_FinalConclusion.PASS.getCode();
                }else if(StringUtils.isNotBlank(routeNo.getId()) && actRouteBo.getId().equals(routeNo.getId())){
                    finalConclusion = BflowConstant.RunInstance_FinalConclusion.NOPASS.getCode();
                }

                if(StringUtils.isNotBlank(finalConclusion)){
                    BflowRunInstanceBo updateInstanceBo = new BflowRunInstanceBo();
                    updateInstanceBo.setId(instanceId);
                    updateInstanceBo.setFinalConclusion(finalConclusion);
                    updateInstanceBo.setUpdateTime(new Date());
                    System.out.println("更新流程最终结论，更新数据：" + JsonUtil.toStr(updateInstanceBo));
                    iBflowRunInstanceService.update(updateInstanceBo);
                }
            }
        }
    }

    /**
     * 查询我的请假历史记录列表
     *
     * @param queryParam
     * @param busiKey
     * @return
     * @throws BizException
     */
    @Override
    public PageResult<BflowRunInstanceBo> findMyHistoryList(BflowRunInstanceQueryParam queryParam, String busiKey, String userId) throws BizException {
        AssertUtil.isNotBlank(busiKey, "流程业务KEY 不能为空");
        AssertUtil.isNotBlank(userId, "用户ID 不能为空");
        BflowRunInstanceBo queryBo = queryParam.getBoIfNullAndNew();
        queryBo.setBusiKey(busiKey);
        queryBo.setStartUid(userId);
        queryParam.setBflowRunInstance(queryBo);
        queryParam.setDefParam(pageNo, pageSize, "CREATE_TIME", "DESC");
        PageResult<BflowRunInstanceBo> pageResult = iBflowRunInstanceService.findPage(queryParam);
        return pageResult;
    }

    /**
     * 查询抄送列表
     * @param queryParam
     * @param procKey 流程KEY
     * @param copyToIntervalType 抄送类型
     * @param copyToUserId 抄送人ID
     * @param finalConclusion 流程最终结论
     * @return
     * @throws BizException
     */
    @Override
    public PageResult<BflowDefineProcessBo> findCopyToList(BflowDefineProcessQueryParam queryParam, String procKey, String copyToIntervalType, String copyToUserId, String finalConclusion) throws BizException {
        AssertUtil.isNotBlank(procKey, "流程KEY 不能为空");
        AssertUtil.isNotBlank(copyToIntervalType, "抄送类型 不能为空");
        AssertUtil.isNotBlank(copyToUserId, "抄送人 不能为空");
        AssertUtil.isNotBlank(finalConclusion, "流程最终结论 不能为空");
        BflowDefineProcessBo queryBo = queryParam.getBoIfNullAndNew();
        queryBo.setStatus(CommConst.STATUS_Y);
        queryBo.setKey(procKey);
        queryBo.setCopyToIntervalType(copyToIntervalType);
        queryBo.setCopyTo(copyToUserId);

        BflowRunInstanceBo instanceQueryBo = new BflowRunInstanceBo();
        instanceQueryBo.setFinalConclusion(finalConclusion);
        queryBo.setInstance(instanceQueryBo);

        queryParam.setBflowDefineProcess(queryBo);
        queryParam.setDefParam(pageNo, pageSize, "CREATE_TIME", "DESC");
        PageResult<BflowDefineProcessBo> pageResult = iBflowDefineProcessService.findProcessInstancePage(queryParam);
        return pageResult;
    }

}